package com.xuanyuanxinyu.core.coroutines

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.async
import kotlinx.coroutines.cancel
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.buffer
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.transform
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import kotlinx.coroutines.withTimeout
import kotlin.time.ExperimentalTime
import kotlin.time.measureTime

/**
 * @FileName:
 * @Description:协程
 * @author ken.luo
 * @date 2023/4/4
 * @version V1.0
 * @email 424533553@qq.com
 */
object CoroutinesUtils {
    /**
     * 方式一:使用runBlocking顶层函数
     * 通常适用于单元测试的场景，而业务开发中不会用到这种方法，因为它是线程阻塞的。就是他会阻塞开启协程的那个线程
     */
    fun startSuspendFunc() {
        runBlocking {
            getNetData()
        }
    }
    
    /**
     * 方式二:使用 GlobalScope 单例对象,直接调用launch开启协程
     * 和runBlocking的区别在于不会阻塞线程,但是在Android中不推荐使用这种方法,因为他的生命周期会与进程一致,而且不能取消
     */
    fun startSuspendFunc2() {
        GlobalScope.launch {
            getNetData()
        }
    }
    
    /**
     * 方式三:自行通过 CoroutineContext 创建一个 CoroutineScope 对象
     * 属于手动开启线程中最常用的方法
     */
    fun startSuspendFunc3() {
        val job = Job()
        //  这里的CoroutineScope是一个方法，并不是一个对象
        val scope = CoroutineScope(job)
        scope.launch {
            getImage()
        }
        
        
        scope.launch(Dispatchers.Main) {
            getNetData()
        }
        val deferred = scope.async(Dispatchers.IO) {
            getImage()
        }
    }
    
    /**
     * 比如我们要在子线程请求数据,然后在主线程更新UI
     * 线程切换
     */
    private fun threadChange() {
        val job = Job()
        val scope = CoroutineScope(job)
        scope.launch(Dispatchers.IO) {
            // 这里是协程(子线程)
            val data = getNetData()
            launch(Dispatchers.Main) {
                // 这里是主线程
                updateUI(data)
            }
        }
    }
    
    /**
     *
     * 这样的代码可以更简单代码搞定
     * coroutineScope.launch(Dispatchers.IO) {
     *     ...
     *     launch(Dispatchers.Main){
     *         ...
     *         launch(Dispatchers.IO) {
     *             ...
     *             launch(Dispatchers.Main) {
     *                 ...
     *             }
     *         }
     *     }
     * }
     */
    private fun startWithContext() {
        // 记得job最终要job.cancel()，这样任务作用域中的协程任务就会停止
        val job = Job()
        val scope = CoroutineScope(job)
        // 通过第二种写法来实现相同的逻辑
        scope.launch(Dispatchers.Main) {
            updateUI("")
            var data : String
            withContext(Dispatchers.IO) {
                data = getNetData()
            }
            
            withContext(Dispatchers.IO) {
                getImage()
            }
            updateUI(data)
        }
    }
    
    /**
     * suspend函数的调用demo
     * 1. 首先该协程是运行在Main线程中的,第一步是刷新UI
     * 2. getRoomData()中设置withContext(Dispatchers.IO),当携程执行到suspend函数时,该协程会切换到IO线程中执行，执行完成后返回Main线程
     * 3. Main线程中继续执行协程中的代码，执行第二次UI刷新
     */
    private fun suspendDemo() {
        val job = Job()
        val scope = CoroutineScope(job)
        scope.launch(Dispatchers.Main) {
            updateUI("")
            val data = getRoomData()
            updateUI(data)
        }
    }
    
    /**
     * 一定需要在UI线程中执行的功能
     */
    private fun updateUI(data : String) {
    
    
    }
    
    /**
     * 一个耗时操作
     */
    private suspend fun getNetData() : String {
        delay(2000) //模拟耗时
        return "Hello"
    }
    
    
    private suspend fun getRoomData() = withContext(Dispatchers.IO) {
        delay(2000) //模拟耗时
        "Hello"
    }
    
    /**
     * 挂载函数
     */
    private suspend fun getImage() {
        // 超时操作，如果超时了，就会进行响应
        //        withTimeoutOrNull()
        // 执行超过时间就会进行异常抛出,使用withTimeoutOrNull 不会抛出异常
        withTimeout(10_1000L) {
            // ...
        }
    }
    
    /**
     * 取消协程任务
     */
    fun cancel() {
        getMainScope().cancel()
    }
    
    
    /**
     * 在Main线程中开启任务
     */
    fun getMainScope() : CoroutineScope {
        //lifecycleScope 的使用
        return MainScope()
    }
    
    private fun test() {
        val scope = MainScope()
        scope.launch { }
    }
    
    /* Kotlin 异步流  ******************************************************************************/
    fun main() = runBlocking {
        launch {
            for (i in 1..4) {
                println("i am not blocked $i")
                delay(100)
            }
        }
        
        simpleFlow().collect {
            print(it)
        }
    }
    
    private fun simpleFlow() : Flow<Int> = flow {
        for (i in 1..4) {
            delay(1000)
            emit(i)
        }
    }
    
    
    fun main2() = runBlocking {
        (1..5).asFlow().filter {
            println("filtering $it")
            it % 2 == 0
        }.map {
            println("mapping $it")
            "string $it"
        }.collect {
            println("collected $it")
        }
    }
    
    /**
     * 将数据转换后发送出去
     */
    fun main3() = runBlocking {
        (1..4).asFlow().transform { request->
            emit("making req $request")
            emit(performReq(request))
        }.collect { response-> println(response) }
    }
    
    private suspend fun performReq(req : Int) : String {
        delay(1000)
        return "response $req"
    }
    
    
    fun /*context_*/main4() = runBlocking {
        simple().flowOn(Dispatchers.Default).collect { value-> print("Collected $value") }
    }
    
    private fun simple() = flow {
        for (i in 1..3) {
            delay(100)
            emit(i)
        }
    }
    
    @OptIn(ExperimentalTime::class)
    fun main5() : Unit = runBlocking {
        val elapse = measureTime {
            simpleFlow2()/*.buffer()*/.collect {
                println("consuming $it")
                delay(200)
                println("consuming done")
            }
            println("all done")
        }
        println("elapse: $elapse")
    }
    
    private fun simpleFlow2() = flow {
        for (i in 1..3) {
            println("producing $i")
            delay(100)
            emit(i)
            println("producing done")
        }
    }
    
    /**
     * 当时间为150时，下游会将1、2、3全部处理,
     * 当时间为300时,下游处理完1时,上游已经发送数据3，则舍弃2不处理
     */
    @OptIn(ExperimentalTime::class)
    fun main6() : Unit = runBlocking {
        val delay = 150L // 300L
        //测量功能区代码执行的时间
        val elapse = measureTime {
            simpleFlow2()/*.buffer()*/.conflate().collect {
                println("consuming $it")
                delay(delay)
                println("consuming $it done")
            }
            println("all done")
        }
        println("elapse: $elapse")
    }
    
    /**
     *  collectLatest 下游取消正在处理的任务,仅处理最新值
     */
    @OptIn(ExperimentalTime::class)
    fun main7() : Unit = runBlocking {
        val delay = 150L
        val elapse = measureTime {
            simpleFlow().buffer().conflate().collectLatest {
                println("consuming $it")
                delay(delay)
                println("consuming $it done")
            }
            println("all done")
        }
        
        println("elapse: $elapse")
    }
    
    /**
     * 创建任务
     */
    private fun createTask() {
    
    }
    
    
}